# -*- coding: utf-8 -*-
"""
@Time    : 2024/7/12 15:10 
@Author  : ZhangShenao 
@File    : aio_download.py 
@Desc    : 使用asyncio异步IO,下载网页内容

asyncio是单线程的,它只有一个主线程,并且在主线程内开启EventLoop,调度多个Coroutine
asyncio比多线程享有更大的自主控制权

await关键字:用于函数声明,标识这是一个异步函数

asyncio.create_task(Coroutine):表示将传入的Coroutine创建一个任务,调度它的执行,并返回此任务对象

asyncio.gather:表示运行所有Coroutine任务

asyncio.run(Coroutine)是Asyncio的root call
它表示拿到event loop,运行输入的Coroutine,直到它结束,最后关闭这个event loop

如果想要用好asyncio,必须有相应的Python库的支持
如aiohttp库支持aio方式的HTTP调用,而requests库则不支持

Asyncio中的任务,在运行过程中不会被打断,因此不会出现race condition的情况
尤其是在I/O密集型场景下,Asyncio比多线程的运行效率更高
因为Asyncio内部任务切换的损耗,远比线程切换的损耗要小
并且Asyncio可以开启的任务数量,也比多线程中的线程数量多得多
"""
import asyncio

import aiohttp

from bacis.decorator.perf_log_decorator import perf_log


async def aio_download_site(url: str) -> None:
    """
    异步执行,从网页下载内容
    :param url: 网页url
    """

    try:
        # 通过aiohttp,发起异步HTTP请求
        async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
            async with session.get(url) as resp:
                print(f'download {resp.content_length} from {url}')

    except Exception as err:
        print(f'download {url} error: {err}')


async def aio_download_all_sites(site_urls: list[str]) -> None:
    """
    异步执行,从多个网页下载内容
    :param site_urls: 网页url列表
    """

    # 创建异步任务
    tasks = [asyncio.create_task(aio_download_site(url)) for url in site_urls]

    # 异步执行任务,等待所有任务执行完成
    await asyncio.gather(*tasks)


@perf_log
def run():
    sites = [
        'https://en.wikipedia.org/wiki/Portal:Arts',
        'https://en.wikipedia.org/wiki/Portal:History',
        'https://en.wikipedia.org/wiki/Portal:Society',
        'https://en.wikipedia.org/wiki/Portal:Biography',
        'https://en.wikipedia.org/wiki/Portal:Mathematics',
        'https://en.wikipedia.org/wiki/Portal:Science',
        'https://en.wikipedia.org/wiki/Computer_science',
        'https://en.wikipedia.org/wiki/Python_(programming_language)',
        'https://en.wikipedia.org/wiki/Java_(programming_language)',
        'https://en.wikipedia.org/wiki/PHP',
        'https://en.wikipedia.org/wiki/Node.js',
        'https://en.wikipedia.org/wiki/The_C_Programming_Language',
        'https://en.wikipedia.org/wiki/Go_(programming_language)'
    ]

    # 执行异步任务
    asyncio.run(aio_download_all_sites(sites))


run()
